package com.dragon.www.haier.service;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Log;

import com.dragon.www.haier.R;
import com.dragon.www.haier.SHApplication;
import com.dragon.www.haier.database.DataHelper;
import com.dragon.www.haier.entity.Device;
import com.dragon.www.haier.model.ActionPacket;
import com.dragon.www.haier.model.DevicePacket;
import com.dragon.www.haier.model.xml.CodeTemplate;
import com.dragon.www.haier.net.AUDPPacket;
import com.dragon.www.haier.net.AppSocket;
import com.dragon.www.haier.net.Utils;
import com.dragon.www.haier.net.WIFIConnectionFactory;
import com.dragon.www.haier.net.WIFIUtils;

import java.io.IOException;
import java.net.BindException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


/**
 * 用于管理WIFI网络入口、客户端通信、数据传输
 * 1.WIFI包头定义：
 * ////////////////////////////////////////////
 * //ID int|NAME String|IP String|Length int//
 * ///////////////////////////////////////////
 * 2.客户端通信：
 * ////////////////////////////////////////////
 * /长连接方式，接包和发包需要传入当前的SOCKET对象/
 * ////////////////////////////////////////////
 * Created by Administrator on 2015/7/17.
 * Author: Songxy
 * Email: thinkdata@163.com
 */
public class UdpService extends Service {
    public final static int MSG_TIME_UPDATED_STATUS = 0;
    public final static int MSG_SYSTEM_STATUS = 1;
    public final static int MSG_DEVICE_IP_FORMAT_ERROR = 2;
    public final static int MSG_DEVICE_UI_UPDATED = 101;
    public final static int MSG_WIFI_STATED_CONNECTED = 105;
    public final static int MSG_LOGIN = 106;
    public final static int MSG_LOGIN_OFF = 107;
    /**
     *
     */
    public final static int SOCKET_PORT = 8000;
    /**
     *
     */
    public final static int MSG_CONNECT_TIMEOUT_REMOTE_BOX = 2001;
    /**
     *
     */
    public final static int MSG_CONNECT_SUCCESSFUL_REMOTE_BOX = 2002;
    /**
     *
     */
    public final static int MSG_CONNECTING_REMOTE_BOX = 2003;
    /**
     *
     */
    public final static int MSG_CONNECT_DISCONNECT_REMOTE_BOX = 2004;
    /**
     *
     */
    public final static int MSG_CONNECT_NAMEERROR_REMOTE_BOX = 2005;
    /**
     *
     */
    public final static int MSG_CONNECT_PASSERROR_REMOTE_BOX = 2006;

    public final static int MSG_REFRESH_BOX_STATUS = 3001;
    public final static int MSG_SEND_BOX_ACTION = 3002;

    /**
     * 存放地址和线程对应关系
     */
    public static final Map<String, UDPSocketConnectRunnable> allSocketsMap = new HashMap<>();
    private final static String TAG = UdpService.class.getName();
    /**
     * UDP连接超时
     */
    private final static int DATAGRAM_SOCKET_TIMEOUT = 5000;
    /**
     *
     */
    private final static int DATAGRAM_SOCKET_BUFFER_SIZE = 8192;
    /**
     * 用于监听业务数据
     */
    private final static WIFIConnectionFactory wifiConnection = WIFIConnectionFactory.getInstance();
    /**
     * 盒子控制线程池
     *
     * @param handler
     */
    private static ExecutorService executorService;
    /**
     *
     */
    private static String deviceId;
    /**
     * 间隔时间单位秒
     */
    private static int interval_connect;
    /**
     * 主UI线程handler
     */
    private static Handler mHandler;
    private static WifiInfo wifiInfo;
    /**
     * 本地wifi网络
     */
    private static UdpConnectStatus currentWifiStatus = UdpConnectStatus.DisConnected;
    /**
     * 编码模板
     */
    private static CodeTemplate codeTemplate;
    /**
     * 本地
     */
    private static DatagramSocket datagramSocket = null;
    /**
     * 本地Socket
     */
    private static AppSocket mAppSocket;
    /**
     * 本地wifi通讯线程，负责各种消息的推送与接收
     */
    private static WifiThread wifiThread = null;
    private BridgeLoopThread bridgeThread = null;
    public static Handler serviceHandler = null;
    private final NetGroupReceiver mNetReceiver = new NetGroupReceiver();
    /**
     *
     */
    private ServiceBinder binder;
    private WifiManager wifiManager;


    public static WIFIConnectionFactory.WIFIObservable getWifiObserver() {
        return wifiConnection.getObservable();
    }

    /**
     * @param packet
     * @return
     */
    public static boolean handlerSendPacket(final AUDPPacket packet) {
        serviceHandler.obtainMessage(MSG_SEND_BOX_ACTION, packet).sendToTarget();
        return true;
    }

    //启动盒子线程
    public static void startBoxesThread(final List<Device> boxes) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }

                for (Device device : boxes) {
                    try {
                        String ip = device.getIp();
                        if (!com.dragon.www.haier.util.Utils.isboolIp(ip)) {
                            mHandler.obtainMessage(MSG_DEVICE_IP_FORMAT_ERROR,
                                    device.getDeviceName()+" IP: \\'"+ip+"\\'格式出错，无法启动通讯。").sendToTarget();
                            continue;
                        }
                        SocketAddress address = new InetSocketAddress(InetAddress.getByAddress(
                                device.getDeviceName(), Utils.convertIpToBytes(ip)), SOCKET_PORT);
                        UDPSocketConnectRunnable runnable = new UDPSocketConnectRunnable(address);
                        allSocketsMap.put(ip, runnable);
                        executorService.execute(runnable);
                    } catch (IOException e) {
                        String msgErr = Log.getStackTraceString(e);
                        Log.e(TAG, msgErr);
                        Message msg = mHandler.obtainMessage(MSG_SYSTEM_STATUS);
                        msg.obj = msgErr;
                        msg.sendToTarget();
                    } catch (RuntimeException e) {

                    }
                }
            }
        }).start();
    }

    public static UdpConnectStatus getWifiState() {
        return currentWifiStatus;
    }

    //重新连接盒子
    public static void reConnectToBox(@NonNull final Device box) {
        //校验ip合法性
        if (!checkIp(box.getIp())) {
            mHandler.obtainMessage(MSG_DEVICE_IP_FORMAT_ERROR,
                    box.getDeviceName()+" IP: \\'"+box.getIp()+"\\'格式出错，无法启动通讯。").sendToTarget();
            return;
        }
        //启动
        final String ip = box.getIp();

        synchronized (allSocketsMap) {
            if (allSocketsMap.containsKey(ip)) {
                UDPSocketConnectRunnable runnable = allSocketsMap.get(ip);
                if (runnable != null) {
                    AUDPPacket packet = DevicePacketFactory.getDevicePacket(
                            DevicePacketFactory.DevicePacketType.LOGIN);
                    try {
                        packet.setSocketAddress(new InetSocketAddress(InetAddress.getByAddress(
                                box.getDeviceName(), Utils.convertIpToBytes(ip)), SOCKET_PORT));
                        serviceHandler.obtainMessage(MSG_SEND_BOX_ACTION, packet).sendToTarget();
                        //通知主线程
                        Message msg = mHandler.obtainMessage(MSG_CONNECTING_REMOTE_BOX);
                        msg.sendToTarget();
                    } catch (IOException e) {

                    }
                }
            // 创建资源
            } else {
                try {
                    SocketAddress address = new InetSocketAddress(InetAddress.getByAddress(
                            box.getDeviceName(), Utils.convertIpToBytes(ip)), SOCKET_PORT);
                    UDPSocketConnectRunnable runnable = new UDPSocketConnectRunnable(address);
                    allSocketsMap.put(ip, runnable);
                    executorService.execute(runnable);
                }catch (IOException e) {
                }

            }
        }
    }

    private static boolean checkIp(String ip) {
        return ip != null && com.dragon.www.haier.util.Utils.isboolIp(ip);
    }

    public static void disConnectToBox(@NonNull final String ipAddr) {
        synchronized (allSocketsMap) {
            AUDPPacket packet = DevicePacketFactory.getDevicePacket(
                    DevicePacketFactory.DevicePacketType.LOGINOFF);
            try {
                wifiThread.sendMsgToNetwork(packet);
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }

    private static UDPSocketConnectRunnable findRunnableByAddr(String ipAddr) {
        synchronized (allSocketsMap) {
            UDPSocketConnectRunnable runnable = allSocketsMap.get(ipAddr);
            if (runnable != null) {
                return runnable;
            }
        }
        return null;
    }

    public void setHandler(Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //DeviceId
        deviceId = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
        interval_connect = this.getResources().getInteger(R.integer.interval_connect);

        //wifi初始化
        wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
        wifiInfo = wifiManager.getConnectionInfo();
        codeTemplate = ((SHApplication) UdpService.this.getApplication()).getCodeTemplate();
        //初始化UDP
        initUDP();
        //
        if (executorService == null) {
            executorService = Executors.newCachedThreadPool();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");

        executorService.shutdown();
        try {
            // bug 新增线程，但是已经关闭线程池
            executorService.awaitTermination(1000, TimeUnit.SECONDS);
        }catch (InterruptedException e) {

        }
        this.allSocketsMap.clear();

        if (wifiThread != null) {
            wifiThread.close();
            wifiThread.interrupt();
            wifiThread = null;
        }
        super.onDestroy();
    }

    public IBinder onBind(Intent intent) {
        Log.i(TAG, "WIFIService onBind");
        binder = new ServiceBinder();
        //
        registerReceiver(mNetReceiver, getFilter());
        //
        if (executorService != null && executorService.isShutdown()) {
            executorService = Executors.newCachedThreadPool();
        }
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "WIFIService unBind");
        unregisterReceiver(mNetReceiver);
        return false;
    }

    private IntentFilter getFilter() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
        filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        return filter;
    }

    /**
     * 初始化与所有智能盒子间的通信
     * 单个盒子建立一个UDP连接
     */
    private void initUDP() {
        bridgeThread = new BridgeLoopThread();
        bridgeThread.start();
        wifiThread = new WifiThread();
        wifiThread.start();
    }

    private static void refreshBoxStatus(int result, String ip) {
        UDPSocketConnectRunnable runnable = findRunnableByAddr(ip);
        UdpConnectStatus status;
        if (result == 0) {
            Log.d(TAG, "用户名登录成功");
            status = UdpConnectStatus.Connected;
            mHandler.obtainMessage(MSG_CONNECT_SUCCESSFUL_REMOTE_BOX).sendToTarget();
        } else {
            mHandler.obtainMessage(MSG_CONNECT_DISCONNECT_REMOTE_BOX).sendToTarget();
            status = UdpConnectStatus.DisConnected;
            if (result == 1) {
                Log.d(TAG, "用户名不存在，登录失败");
            } else if (result == 2) {
                Log.d(TAG, "密码错误，登录失败");
            } else if (result == 3) {
                Log.d(TAG, "超过最大登录数");
            }
        }
        runnable.currentStatus = status;
    }

    //获取盒子线程的网络状态
    public UdpConnectStatus getBoxConnectStatus(@NonNull SocketAddress address) {
        if (this.allSocketsMap.containsKey(address)) {
            UDPSocketConnectRunnable runnable = this.allSocketsMap.get(address);
            return runnable.getConnectStatus();
        } else {
            return UdpConnectStatus.DisConnected;
        }
    }

    //停止盒子线程的其他资源
    public boolean stopBoxRunnable(@NonNull SocketAddress address) {
        if (this.allSocketsMap.containsKey(address)) {
            UDPSocketConnectRunnable runnable = this.allSocketsMap.get(address);
            return true;
        }
        return false;
    }

    /**
     * 网络连接状态
     */
    public enum UdpConnectStatus {
        Connected, DisConnected, Connecting;

        @Override
        public String toString() {
            return super.toString();
        }
    }

    public class BridgeLoopThread extends Thread {

        @Override
        public void run() {
            Looper.prepare();
            serviceHandler = new Handler() {
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSG_REFRESH_BOX_STATUS:
                            Bundle bundle = msg.getData();
                            String ip = bundle.getString("IP");
                            int result = bundle.getInt("RESULT");
                            refreshBoxStatus(result, ip);
                            break;
                        case MSG_SEND_BOX_ACTION:
                            AUDPPacket packet = (AUDPPacket) msg.obj;
                            try {
                                wifiThread.sendMsgToNetwork(packet);
                            }catch (Exception e) {
                                Log.e(TAG, "发送数据失败："+e.getMessage());
                            }
                            break;
                        case MSG_LOGIN_OFF:
                            mHandler.obtainMessage(MSG_LOGIN_OFF).sendToTarget();
                            break;
                        default:
                    }
                }
            };
            Looper.loop();
        }
    }

    private class WifiThread extends Thread {
        boolean flags = true;

        public WifiThread() {
            super("WifiThread");
        }

        public void close() {
            flags = false;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }

            //未介入wifi
            if (!WIFIUtils.existActiveWifi(wifiInfo)) {
                mHandler.obtainMessage(MSG_WIFI_STATED_CONNECTED, -1).sendToTarget();
                synchronized (this) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }

            try {
                datagramSocket = new DatagramSocket(null);
                datagramSocket.setReuseAddress(true);
//                datagramSocket.setSendBufferSize(320);
//                datagramSocket.setReceiveBufferSize(320);
                datagramSocket.bind(new InetSocketAddress(
                        InetAddress.getByName(Utils.convertIntIpAddress(wifiInfo.getIpAddress())), SOCKET_PORT));
            } catch (IOException e) {
                if (e instanceof BindException) {
                }
            }

            if (datagramSocket != null) {
                currentWifiStatus = UdpConnectStatus.Connected;
                Log.d(TAG, "本地Wifi已接入");
                //初始化
                mAppSocket = new AppSocket(datagramSocket);
                //启动所有盒子线程
                startBoxesThread(DataHelper.getInstance().getControlDevice());
                Log.d(TAG, "启动盒子线程");
                //处理接收事件
                doHandlerCallback();
            }
        }

        void doHandlerCallback() {
            while (flags) {
                try {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        Log.e(TAG, "" + e.getMessage());
                    }

                    // TODO: 2016/3/16 处理返回操作指令
                    doHandler(receivePacket());

                } catch (Exception e) {
                    if (e instanceof SocketTimeoutException) {
                        Log.e(TAG, " 连接超时");
                        mHandler.obtainMessage(MSG_CONNECT_TIMEOUT_REMOTE_BOX).sendToTarget();
                    } else {
                        Log.e(TAG, "", e);
                    }
                }
            }
        }

        void doHandler(AUDPPacket packet) {
            //事件派发
            wifiConnection.getObservable().handler(packet);
        }

        /**
         * 由于协议没有区分包头、包体，需要获取裸包，并按照action处理
         *
         * @return
         * @throws IOException
         */
        public DevicePacket receivePacket() throws Exception {
            DevicePacket devicePacket = codeTemplate.decodePacket(new ActionPacket(), mAppSocket);
            if (devicePacket == null) return null;

            String ip = ((InetSocketAddress) devicePacket.getSocketAddress()).getAddress().getHostName();
            Bundle b = new Bundle();
            if (Arrays.equals(devicePacket.getAction(), DevicePacketFactory.KEY_ACTION_DATAGRAM_TYPE_LOGIN)) {
                int result = Utils.byte2Int(devicePacket.getPacketData());
                Message msg = Message.obtain(serviceHandler, MSG_REFRESH_BOX_STATUS);
                b.putString("IP", ip);
                b.putInt("RESULT", result);
                msg.setData(b);
                msg.sendToTarget();
                return null;
            } else if (Arrays.equals(devicePacket.getAction(), DevicePacketFactory.KEY_ACTION_DATAGRAM_TYPE_QUIT)) {
                int result = Utils.byte2Int(devicePacket.getPacketData());
                if (result == 0) {
                    //退出线程还继续保持
//                    currentStatus = UdpService.UdpConnectStatus.DisConnected;
                    Message.obtain(serviceHandler, MSG_LOGIN_OFF).sendToTarget();
                }
            } else if (Arrays.equals(devicePacket.getAction(), DevicePacketFactory.KEY_ACTION_DATAGRAM_TYPE_DETECT)) {
                int result = Utils.byte2Int(devicePacket.getPacketData());
                if (result == 0) {
                }
                return null;
            } else if (Arrays.equals(devicePacket.getAction(), DevicePacketFactory.KEY_ACTION_DATAGRAM_TYPE_ACTION_DETECT_2)) {
                byte[] data = devicePacket.getPacketData();
                return null;
            } else if (Arrays.equals(devicePacket.getAction(), DevicePacketFactory.KEY_ACTION_DATAGRAM_TYPE_ACTION_DETECT_1)) {
                byte[] data = devicePacket.getPacketData();
            }

            return devicePacket;
        }

        void sendMsgToNetwork(AUDPPacket packet) throws IOException {
            mAppSocket.sendMsgToNetwork(packet, false);
        }
    }

    /**
     * 维护单个UDP连接
     */
    public static class UDPSocketConnectRunnable implements Runnable {

        String uuid = null;
        SocketAddress address;
        UdpConnectStatus currentStatus = UdpConnectStatus.DisConnected;
        /**
         * 按时间间隔重新连接盒子
         */
        Timer timer = new Timer(true);

        public UDPSocketConnectRunnable(@NonNull SocketAddress address) throws IOException {
            uuid = UUID.randomUUID().toString();
            this.address = address;
        }

        public String getId() {
            return uuid;
        }

        public UdpConnectStatus getConnectStatus() {
            return currentStatus;
        }

        void connectToRemoteBox() {

            try {
                AUDPPacket packet = DevicePacketFactory.getDevicePacket(
                        DevicePacketFactory.DevicePacketType.LOGIN);
                for (int i = 0; i < 3; i++) {
                    packet.setSocketAddress(this.address);
                    mAppSocket.sendMsgToNetwork(packet);
                }
                //通知主线程
                Message msg = mHandler.obtainMessage(MSG_CONNECTING_REMOTE_BOX);
                msg.obj = address;
                msg.sendToTarget();
            } catch (IOException e) {
            }

            final AUDPPacket statusPacket = DevicePacketFactory.getDevicePacket(
                    DevicePacketFactory.DevicePacketType.HEARTBEAT);

            //
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        statusPacket.setSocketAddress(address);
                        mAppSocket.sendMsgToNetwork(statusPacket);
                    } catch (SocketException e) {
                        mHandler.obtainMessage(MSG_CONNECT_TIMEOUT_REMOTE_BOX).sendToTarget();
                    } catch (IOException e) {
                        mHandler.obtainMessage(MSG_CONNECT_TIMEOUT_REMOTE_BOX).sendToTarget();
                    } finally {
                    }
                }
            }, 1000, interval_connect * 1000);
        }



        @Override
        public void run() {
            connectToRemoteBox();
        }
    }


    public class ServiceBinder extends Binder {
        public UdpService getService() {
            return UdpService.this;
        }
    }

    private class NetGroupReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
//            ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//            WifiManager wifiManager = (WifiManager) context.getSystemService(WIFI_SERVICE);
            //监听wifi网络变化
            if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                int temp = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
                WifiInfo wifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
                if (WIFIUtils.existActiveWifi(wifiInfo)) {
                    currentWifiStatus = UdpConnectStatus.Connected;
                    mHandler.obtainMessage(MSG_WIFI_STATED_CONNECTED).sendToTarget();
                    // TODO: 2016/3/14 通知线程连接各个盒子
                    if (wifiThread != null) {
                        synchronized (wifiThread) {
                            wifiThread.notifyAll();
                        }
                    }
                }
                Log.e(TAG, "Wifi Status: " + temp);
            } else if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {

            } else if (intent.getAction().equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
                Boolean state = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
                if (state) {
                    currentWifiStatus = UdpConnectStatus.Connected;
                    mHandler.obtainMessage(MSG_WIFI_STATED_CONNECTED).sendToTarget();
                    // TODO: 2016/3/14 通知线程连接各个盒子
                    if (wifiThread != null) {
                        synchronized (wifiThread) {
                            wifiThread.notifyAll();
                        }
                    }
                }
                Log.e(TAG, "NetworkInfo connected Statue: " + state);
            } else if (intent.getAction().equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
                SupplicantState state = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
                Log.e(TAG, "NetworkInfo connected Statue: " + state);
            } else {

            }
        }
    }
}
